
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <openssl/ssl.h>
#include <auth-client.h>
#include "email.h"
#include <libesmtp.h>
//#define USE_GLIB
#ifndef SOFTWARE_NAME
 #define SOFTWARE_NAME "example"
#endif
#ifndef VERSION
 #define VERSION "NO VERSION"
#endif
#ifndef USE_GLIB
//#define pwd "544867664robot"
#define pwd "ugpfieiqfmvtbihh" //210214599@qq.com
/*
int
tlsinteract (char *buf, int buflen, int rwflag, void *arg)
{
  printf("%s\n", __func__);
  char *pw;
  int len;

  pw = pwd;
  len = strlen (pw);
  if (len + 1 > buflen)
    return 0;
  strcpy (buf, pw);
  return len;
}
*/
int authinteract (auth_client_request_t request, char **result,
    int fields, void *arg)
{
  printf("%s\n", __func__);
  int i;
  char **userpwd = (char **)arg;
  printf("user:%s pwd:%s\n", userpwd[0], userpwd[1]);

  for (i = 0; i < fields; i++) {
    if (request[i].flags & AUTH_PASS) {
      result[i] = userpwd[1];
    } else {
      result[i] = userpwd[0];
    }
  }
  return 1;
}
/*
int
handle_invalid_peer_certificate(long vfy_result)
{
  const char *k ="rare error";
  switch(vfy_result) {
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"; break;
  case X509_V_ERR_UNABLE_TO_GET_CRL:
    k="X509_V_ERR_UNABLE_TO_GET_CRL"; break;
  case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
    k="X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"; break;
  case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
    k="X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"; break;
  case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
    k="X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"; break;
  case X509_V_ERR_CERT_SIGNATURE_FAILURE:
    k="X509_V_ERR_CERT_SIGNATURE_FAILURE"; break;
  case X509_V_ERR_CRL_SIGNATURE_FAILURE:
    k="X509_V_ERR_CRL_SIGNATURE_FAILURE"; break;
  case X509_V_ERR_CERT_NOT_YET_VALID:
    k="X509_V_ERR_CERT_NOT_YET_VALID"; break;
  case X509_V_ERR_CERT_HAS_EXPIRED:
    k="X509_V_ERR_CERT_HAS_EXPIRED"; break;
  case X509_V_ERR_CRL_NOT_YET_VALID:
    k="X509_V_ERR_CRL_NOT_YET_VALID"; break;
  case X509_V_ERR_CRL_HAS_EXPIRED:
    k="X509_V_ERR_CRL_HAS_EXPIRED"; break;
  case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
    k="X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"; break;
  case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
    k="X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"; break;
  case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
    k="X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"; break;
  case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
    k="X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"; break;
  case X509_V_ERR_OUT_OF_MEM:
    k="X509_V_ERR_OUT_OF_MEM"; break;
  case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
    k="X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"; break;
  case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
    k="X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"; break;
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
    k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"; break;
  case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
    k="X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"; break;
  case X509_V_ERR_CERT_CHAIN_TOO_LONG:
    k="X509_V_ERR_CERT_CHAIN_TOO_LONG"; break;
  case X509_V_ERR_CERT_REVOKED:
    k="X509_V_ERR_CERT_REVOKED"; break;
  case X509_V_ERR_INVALID_CA:
    k="X509_V_ERR_INVALID_CA"; break;
  case X509_V_ERR_PATH_LENGTH_EXCEEDED:
    k="X509_V_ERR_PATH_LENGTH_EXCEEDED"; break;
  case X509_V_ERR_INVALID_PURPOSE:
    k="X509_V_ERR_INVALID_PURPOSE"; break;
  case X509_V_ERR_CERT_UNTRUSTED:
    k="X509_V_ERR_CERT_UNTRUSTED"; break;
  case X509_V_ERR_CERT_REJECTED:
    k="X509_V_ERR_CERT_REJECTED"; break;
  }
  printf("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld: %s\n", vfy_result, k);
  return 1;
}
void
print_recipient_status (smtp_recipient_t recipient,
                        const char *mailbox, void *arg)
{
  const smtp_status_t *status;

  status = smtp_recipient_status (recipient);
  printf ("%s: %d %s", mailbox, status->code, status->text);
}
void event_cb (smtp_session_t session, int event_no, void *arg,...)
{
  va_list alist;
  int *ok;

  va_start(alist, arg);
  switch(event_no) {
  case SMTP_EV_CONNECT:
  case SMTP_EV_MAILSTATUS:
  case SMTP_EV_RCPTSTATUS:
  case SMTP_EV_MESSAGEDATA:
  case SMTP_EV_MESSAGESENT:
  case SMTP_EV_DISCONNECT: break;
  case SMTP_EV_WEAK_CIPHER: {
    int bits;
    bits = va_arg(alist, long); ok = va_arg(alist, int*);
    printf("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.\n", bits);
    *ok = 1; break;
  }
  case SMTP_EV_STARTTLS_OK:
    puts("SMTP_EV_STARTTLS_OK - TLS started here."); break;
  case SMTP_EV_INVALID_PEER_CERTIFICATE: {
    long vfy_result;
    vfy_result = va_arg(alist, long); ok = va_arg(alist, int*);
    *ok = handle_invalid_peer_certificate(vfy_result);
    break;
  }
  case SMTP_EV_NO_PEER_CERTIFICATE: {
    ok = va_arg(alist, int*);
    puts("SMTP_EV_NO_PEER_CERTIFICATE - accepted.");
    *ok = 1; break;
  }
  case SMTP_EV_WRONG_PEER_CERTIFICATE: {
    ok = va_arg(alist, int*);
    puts("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted.");
    *ok = 1; break;
  }
  case SMTP_EV_NO_CLIENT_CERTIFICATE: {
    ok = va_arg(alist, int*);
    puts("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted.");
    *ok = 1; break;
  }
  default:
    printf("Got event: %d - ignored.\n", event_no);
  }
  va_end(alist);
}
*/
void *email_send1(const char *hostport, const char *mailbox,
    const char *recv, const char *msg)
{
  smtp_session_t session;
  smtp_message_t message;
  const smtp_status_t *status;
  auth_context_t authctx;
  smtp_recipient_t recipient;
  const char *userpwd[2];
  userpwd[0] = mailbox;
  userpwd[1] = pwd;

  auth_client_init ();
  authctx = auth_create_context ();
  session = smtp_create_session();
  message = smtp_add_message(session);

  auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0);
  //auth_set_interact_cb (authctx, authinteract, NULL);
  auth_set_interact_cb (authctx, authinteract, userpwd);
  //smtp_starttls_set_password_cb (tlsinteract, NULL);
  smtp_auth_set_context (session, authctx);
  auth_client_enabled(authctx);


  //smtp_set_eventcb(session, event_cb, NULL);
  //smtp_starttls_enable (session, Starttls_ENABLED);
  smtp_starttls_enable (session, Starttls_REQUIRED);
  smtp_set_server(session, hostport);
  smtp_set_reverse_path(message, mailbox);
  recipient = smtp_add_recipient(message, recv);
  smtp_dsn_set_notify (recipient, Notify_NOTSET);
  smtp_set_header (message, "To", NULL, NULL);
  smtp_set_header(message, "Subject", "Test Email");
  smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1);
  smtp_set_message_str(message, (void *)msg);
  if (!smtp_start_session (session)) {
    char buf[128];
    fprintf (stderr, "SMTP server problem %s\n",
        smtp_strerror (smtp_errno (), buf, sizeof buf));
  } else {
    status = smtp_message_transfer_status (message);
    printf ("%d %s", status->code,
        (status->text != NULL) ? status->text : "\n");
    //smtp_enumerate_recipients (message, print_recipient_status, NULL);
  }
  smtp_destroy_session (session);
  auth_destroy_context (authctx);
  auth_client_exit ();
  return NULL;
}
#else
#include <gio/gio.h>
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void * email_read (void *istream)
{
  char buffer[1024] = {0};
  GError *error;
  gsize size;

  do {
    size = g_input_stream_read (istream, buffer, sizeof buffer, NULL, &error);
    if (size < 0) {
      g_printerr ("Error receiving from socket: %s\n", error->message);
      goto out;
    }
  } while (size < 1);
  g_print ("L%d f-%s++++%d:%.*s\n", __LINE__, __func__, (int)size, (int)size, buffer);
out:
  return g_strdup (buffer);
}
int email_write (void *ostream, const char *buf, int flag)
{
  gsize size;
  GError *error;
  char *buffer = NULL;

  if (flag) {
    buffer = g_base64_encode((guchar *)buf, strlen(buf));
    size = g_output_stream_write (ostream, buffer, strlen(buffer), NULL, &error);
  } else {
    size = g_output_stream_write (ostream, buf, strlen(buf), NULL, &error);
  }
  if (size < 1) {
          g_printerr ("Error receiving from socket: %s\n",
                      error->message);
    goto out;
        }
out:
  if (buffer) {
    g_print ("L%d f-%s----%d:%.*s\n", __LINE__, __func__,(int)size, (int)size,
        buffer);
    g_free(buffer);
  } else {
    g_print ("L%d f-%s----%d:%.*s\n", __LINE__, __func__, (int)size, (int)size, buf);
  }
  size = g_output_stream_write (ostream, "\r\n", 2, NULL, &error);
  return size;
}
static gboolean make_connection (const char       *argument,
                 GTlsCertificate  *certificate,
                 GCancellable     *cancellable,
                 GSocket         **socket,
                 GSocketAddress  **address,
                 GIOStream       **connection,
                 GInputStream    **istream,
                 GOutputStream   **ostream,
                 GError          **error)
{
  GSocketAddressEnumerator *enumerator;
  GSocketConnectable *connectable;
  GSocketAddress *src_address;
//  GTlsInteraction *interaction;
  GError *err = NULL;
  int read_timeout = 100;

  *socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, 0, error);
  g_socket_set_timeout (*socket, read_timeout);
  connectable = g_network_address_parse (argument, 7777, error);
  if (connectable == NULL)
    return FALSE;

  enumerator = g_socket_connectable_enumerate (connectable);
  while (TRUE)
    {
      *address = g_socket_address_enumerator_next (enumerator, cancellable, error);
      if (*address == NULL)
        {
          if (error != NULL && *error == NULL)
            g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                                 "No more addresses to try");
          return FALSE;
        }

      if (g_socket_connect (*socket, *address, cancellable, &err))
        break;
      //g_message ("Connection to %s failed: %s, trying next", socket_address_to_string (*address), err->message);
      g_clear_error (&err);

      g_object_unref (*address);
    }
  g_object_unref (enumerator);
//  g_print ("Connected to %s\n", socket_address_to_string (*address));

  src_address = g_socket_get_local_address (*socket, error);
  if (!src_address)
    {
      g_prefix_error (error, "Error getting local address: ");
      return 0;
    }

  //g_print ("local address: %s\n",
    //       socket_address_to_string (src_address));
  g_object_unref (src_address);
  *connection = G_IO_STREAM (g_socket_connection_factory_create_connection (*socket));
  g_object_unref (connectable);

  if (*connection)
    {
      *istream = g_io_stream_get_input_stream (*connection);
       *ostream = g_io_stream_get_output_stream (*connection);
    }
  return 1;
}
void email_qq_verification (const char *argument,
    const char *from,
    const char *pwd,
    const char *to,
    const char *data)
{
  GSocket *socket = NULL;
  GSocketAddress *address = NULL;
  GError *error = NULL;
 // GOptionContext *context = NULL;
  GCancellable *cancellable = NULL;
  GIOStream *connection = NULL;
  GInputStream *istream = NULL;
  GOutputStream *ostream = NULL;
  //GSocketAddress *src_address = NULL;
  GTlsCertificate *certificate = NULL;
 // gsize base_len;
  char buf[1024] = {0}, *msg = NULL;//*base, ;
 // gint i;

  address = NULL;
  connection = NULL;
  if (!make_connection ("157.148.54.34:25",
        //argument,
        certificate, cancellable, &socket, &address,
                       &connection, &istream, &ostream, &error))
  {
    g_error ("make connect \n");
    goto out;
  }
  msg = email_read(istream);
  email_write(ostream, "auth login", 0);
  msg = email_read(istream);
  g_print ("L%d f-%s++++%ld:%s\n", __LINE__, __func__, strlen(msg), msg);
  email_write(ostream, from, 1);
  msg = email_read(istream);
  email_write(ostream, pwd, 1);
  msg = email_read(istream);
  email_write(ostream, "HELO DEBIAN", 0);
  msg = email_read(istream);
  g_snprintf(buf, sizeof buf, "mail from:<%s>", from);
  email_write(ostream, buf, 0);
  msg = email_read(istream);
  g_snprintf(buf, sizeof buf, "rcpt to:<%s>", to);
  email_write(ostream, buf, 0);
  msg = email_read(istream);
  email_write(ostream, "data", 0);
  msg = email_read(istream);
  g_snprintf(buf, sizeof buf,
      "subject:hello email\r\nfrom:8300社区<%s>\r\nto:me\r\n\r\n%s\r\n.\r\n",
      from, data);
  email_write(ostream, buf, 0);
  email_write(ostream, "quit", 0);

  if (!g_io_stream_close (connection, cancellable, &error)) {
          g_printerr ("Error closing connection: %s\n",
                      error->message);
          goto out;
        }
  g_object_unref (connection);
  g_print ("closing socket\n");
out:
  g_object_unref (socket);
  g_object_unref (address);

  return;
}
#endif

int main()
{// 210214599 ugpfieiqfmvtbihh
#ifdef USE_GLIB
  char *recv = "210743063qq.com", *msg = "test";
  email_qq_verification ("stmp.qq.com", "210214599@qq.com", "wutffilwggydcaef", recv, msg);
#else
  //email_send("157.148.54.34:25", "210214599@qq.com",
   //   "210743063@qq.com", "test");
  //email_send("157.148.54.34:25",
  email_send(
      //"smtp.qq.com",
      //"210743063@qq.com",
      "210214599@qq.com", "ugpfieiqfmvtbihh",
      "210743063@qq.com", "test");
#endif
    printf("CRC32\n");
    return 0;
}

